package liquor.util;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Collection;
import java.util.Iterator;

import liquor.constant.BigDecimalConstants;

public class BigDecimalUtil {
	private enum Operation{
		MIN, MAX
	}
	
	private static final MathContext MATH_CONTEXT = MathContext.DECIMAL64;
	
	/**
	 * left와 right를 비교하여 같은지 판별해준다.</br>
	 * 만약 left나 right중 하나가 null 이면 false </br>
	 * cf) 0.0 == 0
	 * @param left {@link BigDecimal}
	 * @param right {@link BigDecimal}
	 * @return boolean
	 */
	public static boolean equals(BigDecimal left, BigDecimal right) {
		if (left == null || right == null) {
			return false;
		}
		return left.compareTo(right) == 0;
	}
	
	/**
	 * {@link BigDecimal} 값이 null 이면 0을 반환해준다.
	 * @param value {@link BigDecimal}
	 * @return {@link BigDecimal}
	 */
	public static BigDecimal nullToZero(BigDecimal value) {
		return value == null ? BigDecimalConstants.ZERO : value;
	}
	
	/**
	 * {@link BigDecimal} 비교함수
	 * @param left - {@link BigDecimal}
	 * @param right - {@link BigDecimal}
	 * @return left 가  right 보다 크면 <code>true</code> 아니면 <code>false</code>
	 */
	public static boolean isLeftG(BigDecimal left, BigDecimal right) {
		return left.compareTo(right) > 0;
	}
	
	/**
	 * {@link BigDecimal} 비교함수
	 * @param left - {@link BigDecimal}
	 * @param right - {@link BigDecimal}
	 * @return left 가  right 보다 크거나 같으면 <code>true</code> 아니면 <code>false</code>
	 */
	public static boolean isLeftGE(BigDecimal left, BigDecimal right) {
		return left.compareTo(right) >= 0;
	}
	
	/**
	 * {@link BigDecimal} 비교함수
	 * @param left - {@link BigDecimal}
	 * @param right - {@link BigDecimal}
	 * @return right 가  left 보다 크면 <code>true</code> 아니면 <code>false</code>
	 */
	public static boolean isRightG(BigDecimal left, BigDecimal right) {
		return right.compareTo(left) > 0;
	}
	
	/**
	 * {@link BigDecimal} 비교함수
	 * @param left - {@link BigDecimal}
	 * @param right - {@link BigDecimal}
	 * @return right 가  left 보다 크거나 같으면 <code>true</code> 아니면 <code>false</code>
	 */
	public static boolean isRightGE(BigDecimal left, BigDecimal right) {
		return right.compareTo(left) >= 0;
	}
	
	
	/**
	 * {@link BigDecimal} 덧셈함수
	 * left 또는 right가 null 이면 {@link NullPointerException}
	 * @param left
	 * @param right
	 * @return left + right
	 */
	public static BigDecimal add(BigDecimal left, BigDecimal right) {
		return left.add(right, MATH_CONTEXT);
	}
	
	/**
	 * {@link BigDecimal} 뺄셈함수
	 * left 또는 right가 null 이면 {@link NullPointerException}
	 * @param left
	 * @param right
	 * @return left - right
	 */
	public static BigDecimal subtract(BigDecimal left, BigDecimal right) {
		return left.subtract(right, MATH_CONTEXT);
	}

	/**
	 * {@link BigDecimal} 곱셈함수
	 * left 또는 right가 null 이면 {@link NullPointerException}
	 * @param left
	 * @param right
	 * @return left x right
	 */
	public static BigDecimal multiply(BigDecimal left, BigDecimal right) {
		return left.multiply(right, MATH_CONTEXT);
	}
	
	/**
	 * {@link BigDecimal} 나눗셈함수
	 * left 또는 right가 null 이면 {@link NullPointerException}
	 * @param left
	 * @param right
	 * @return left / right
	 */
	public static BigDecimal divide(BigDecimal left, BigDecimal right) {
		return left.divide(right, MATH_CONTEXT);
	}
	
	
	/**
	 * {@link BigDecimal} 절대값함수
	 * left 또는 right가 null 이면 {@link NullPointerException}
	 * @param value
	 * @return |value|
	 */
	public static BigDecimal abs(BigDecimal value) {
		return value.abs();
	}
	
	
	/**
	 * min 함수 <br>
	 * 인자들중 가장 작은 값을 반환한다
	 * @param args - {@link BigDecimal}
	 * @return 가장 작은 값
	 */
	public static BigDecimal min(BigDecimal...args) {
		return operations(Operation.MIN, args);
	}
	
	/**
	 * min 함수 <br>
	 * 인자들중 가장 작은 값을 반환한다
	 * @param collection - {@link BigDecimal}
	 * @return 가장 작은 값
	 */
	public static BigDecimal min(Collection<BigDecimal> collection) {
		return operations(Operation.MIN, collection);
	}
	
	/**
	 * max 함수 <br>
	 * 인자들중 가장 작은 값을 반환한다
	 * @param args - {@link BigDecimal}
	 * @return 가장 작은 값
	 */
	public static BigDecimal max(BigDecimal...args) {
		return operations(Operation.MAX, args);
	}
	
	/**
	 * max 함수 <br>
	 * 인자들중 가장 작은 값을 반환한다
	 * @param collection - {@link BigDecimal}
	 * @return 가장 작은 값
	 */
	public static BigDecimal max(Collection<BigDecimal> collection) {
		return operations(Operation.MAX, collection);
	}
	

	/*
	 * 연산(컬랙션) 내부함수
	 */
	private static BigDecimal operations(Operation oper, Collection<BigDecimal> collection) {
		if (collection == null || collection.size() <= 0) {
			return null;
		}
		
		Iterator<BigDecimal> iter = collection.iterator();
		BigDecimal ret = iter.next();
		
		while (iter.hasNext()) {
			BigDecimal next = iter.next();
			switch (oper) {	
				case MIN :		ret = ret.compareTo(next) < 0 ? ret : next; break;
				case MAX :		ret = ret.compareTo(next) > 0 ? ret : next; break;
			}
		}
		
		return ret;
	}
	
	/*
	 * 연산(가변인자) 내부함수
	 */
	private static BigDecimal operations(Operation oper, BigDecimal... args) {
		if (args == null || args.length <= 0) {
			return null;
		}
		
		BigDecimal ret = args[0];
		for (int i=1 ; i<args.length ; i++) {
			BigDecimal next = args[i];
			switch (oper) {
			case MIN :		ret = ret.compareTo(next) < 0 ? ret : next; break;
			case MAX :		ret = ret.compareTo(next) > 0 ? ret : next; break;
		}
			
		}
		
		return ret;
	}
	
	/* 생성자 private 처리 */
	private BigDecimalUtil() {}
}
